package com.andy.springboot.interceptor;

import java.io.OutputStream;
import java.lang.reflect.Method;
import java.util.Map;
import java.util.concurrent.TimeUnit;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

import com.alibaba.fastjson.JSON;
import com.andy.springboot.annotation.IgnoreLoginCheck;
import com.andy.springboot.dto.RedisKey;
import com.andy.springboot.dto.ResponseResult;
import com.andy.springboot.dto.ReturnCode;
import com.andy.springboot.exception.ActionException;
import com.andy.springboot.service.common.CacheService;
import com.andy.springboot.utils.MD5Util;
import com.google.common.base.Strings;

/**
 * token、签名校验拦截器
 * @author Andy.ye
 * @date 2018年3月23日 下午3:29:36
 */
public class LoginAnnotationInterceptor extends HandlerInterceptorAdapter {

	private static final Logger LOGGER = LoggerFactory.getLogger(LoginAnnotationInterceptor.class);

	@Resource
	private CacheService cacheService;

	@Override
	public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
			throws Exception {
		if (handler instanceof HandlerMethod) {
			HandlerMethod handler2 = (HandlerMethod) handler;
			Method method = handler2.getMethod();
			LOGGER.info("方法[" + method.getDeclaringClass().getSimpleName() + "." + method.getName() + "()]拦截开始.....");
			try {
				if (method.isAnnotationPresent(RequestMapping.class)) {
					Class<?> clazz = method.getDeclaringClass();
					if (method.isAnnotationPresent(IgnoreLoginCheck.class)
							|| clazz.isAnnotationPresent(IgnoreLoginCheck.class)) {
						return true;
					}
					// skip swagger controller
					if ("ApiResourceController".equals(method.getDeclaringClass().getSimpleName())
							|| "Swagger2Controller".equals(method.getDeclaringClass().getSimpleName())
							|| "BasicErrorController".equals(method.getDeclaringClass().getSimpleName())) {
						return true;
					}

					String token = request.getHeader("token");
					if (Strings.isNullOrEmpty(token)) {
						responseExceptionResult(response, new ActionException(ReturnCode.MISS_REQUIRED_PARAM, "token"));
						return false;
					}
					LOGGER.debug("token: " + token);

					// 检查token
					String tokenKey = String.format(RedisKey.USER_TOKEN, token);
					Map<String, String> userInfoMap = cacheService.getMap(tokenKey);
					if (null == userInfoMap || userInfoMap.isEmpty()) {
						responseExceptionResult(response, new ActionException(ReturnCode.TOKEN_INVALID));
						return false;
					} else {
						// 有操作 更新token过期时间
						cacheService.getRedisTemplate().expire(tokenKey, 90 * 24 * 3600, TimeUnit.SECONDS);
					}

					// 检查签名
					String secret = userInfoMap.get("secret");
					// 客户端传过来签名
					String signature = request.getHeader("signature");
					String timestamp = request.getHeader("timestamp");
					String signatureServer = MD5Util.getMD5String(secret + "#@#@#" + timestamp);
					LOGGER.info("timestamp:" + timestamp + ", secret: " + secret + ", signature:" + signature
							+ ", signatureServer:" + signatureServer);
					if (!StringUtils.endsWithIgnoreCase(signature, signatureServer)) {
						responseExceptionResult(response, new ActionException(ReturnCode.SIGNATURE_ERROR));
						return false;
					}
				}
			} catch (Exception e) {
				LOGGER.error("拦截出错！", e);
			} finally {
				LOGGER.info("方法[" + method.getDeclaringClass().getSimpleName() + "." + method.getName() + "()]拦截结束！");
			}
		}
		return true;

	}

	/**
	 * 包装异常数据 返回給前端
	 * @param response
	 * @param e
	 * @throws Exception
	 */
	private void responseExceptionResult(HttpServletResponse response, ActionException e) throws Exception {
		LOGGER.error(e.getMessage(), e);
		response.setContentType("application/json; charset=utf-8");
		response.setCharacterEncoding("UTF-8");
		ResponseResult result = new ResponseResult();
		result.setData("");
		result.setCode(e.getRtnCode().getCode());
		result.setMessage(String.format(e.getRtnCode().getMessage(), e.getPlaceHolder()));
		String jsonResult = JSON.toJSONString(result);
		OutputStream out = response.getOutputStream();
		out.write(jsonResult.getBytes("UTF-8"));
		out.flush();
	}
}
